// 异步连接
// @author 风杨
// @date 22-9-27 下午5:21
// @see <a href="yueny09@163.com">mailTo: yueny09@163.com</a>
//
use std::thread;

use redis::{Commands, Connection, Client, RedisError, RedisResult, ConnectionLike};
use redis::ToRedisArgs;

use log::info;

use crate::config::{get_value};
use crate::init_config;
use crate::r#const::{KEY_REDIS_HOSTNAME, KEY_REDIS_PASSWORD, KEY_REDIS_PORT, KEY_REDIS_USER_NAME};


/// 异步连接
/// param address:   let address = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 6379));
/// // return is  impl Future<Output = redis::Connection>
pub async fn redis_demo_async() -> Connection {
    let redisUrl = "redis://".to_owned()
        + &*get_value(KEY_REDIS_USER_NAME)
        + ":"
        + &*get_value(KEY_REDIS_PASSWORD)
        + "@"
        + &*get_value(KEY_REDIS_HOSTNAME)
        + ":"
        + &*get_value(KEY_REDIS_PORT);

    let currentHandle = thread::current();
    info!("线程{} 中组装 redis url in main", thread::current().name().unwrap());
    let handler = tokio::spawn(async move {
        // 子线程
        info!("线程{} in spawn", thread::current().name().unwrap());
        new(&redisUrl).await
    });
    info!("线程{} await in main", thread::current().name().unwrap());
    let conn = handler.await.unwrap();



    info!("线程{} get in main", thread::current().name().unwrap());

    conn
}

async fn new(redisUrl: &str) -> Connection {
    let client: Client = redis::Client::open(redisUrl)
        .unwrap();

    info!("线程{} new redis connection in method new", thread::current().name().unwrap());

    client.get_connection().unwrap()
}

#[deny(overflowing_literals)]
struct Point {
    x: u32
}

impl Point {
    pub fn getX(&self) -> u32 {
        return self.x;
    }
}

#[cfg(test)]
mod test {
    use std::borrow::BorrowMut;
    use std::io::Error;
    use std::rc::Rc;
    use std::sync::{Arc, Mutex};
    use std::thread;
    use log::info;
    use redis::{Commands, Connection, ConnectionLike};
    use crate::init_config;
    use crate::redis_con::redis_client_async::{Point, redis_demo_async};

    #[tokio::test]
    async fn test_multi() -> Result<(), Error>{
        info!("test_multi");

        // init 日志
        // 使用 log4rs::init_file 方法读取配置文件进行初始化
        log4rs::init_file("log4rs.yaml", Default::default()).unwrap();
        init_config();

        info!("线程{} in test_multi", thread::current().name().unwrap());
        let mut conn: Connection = redis_demo_async().await;

        assert!(conn.is_open());

        info!("线程{} in test_multi", thread::current().name().unwrap());
        let _ : () = conn.set("my_key1", 66).unwrap();
        // read back the key and return it.  Because the return value
        // from the function is a result for integer this will automatically
        // convert into one.
        let val = conn.get::<String, String>(String::from("my_key1"));
        assert_eq!(val.unwrap(), "66");

        conn.del::<&str, u16>("my_key1").unwrap();

        Ok(())
    }

    /// 多线程测试用例
    #[tokio::test]
    async fn test_thread() -> Result<(), Error>{
        info!("test_thread");

        let mut listVecShard = Vec::new();
        for i in 0..100000 {
            listVecShard.push(Point{x : i});
        }

        println!("listVec length:{}", &listVecShard.len());
        println!("listVec index 66 :{}", &listVecShard.get(66).unwrap().getX());

        // 定义一个Box包装变量, 编译可以在多线程中共享
        // Box<T>，用于在堆上分配值
        // Rc<T>，(reference counter)一个引用计数类型，其数据可以有多个所有者。
        // Arc<T>，(atomic reference counter)可被多线程操作，但只能只读。
        // Mutex<T>，互斥指针，能保证修改的时候只有一个线程参与。
        let mut p = Arc::new(Mutex::new(listVecShard));

        // // // initialize
        // let mut thread_handles = vec![];

        // create threads
        for x in 0..10 {
            // 10 个线程
            let threadName = String::from("thread-".to_owned() + &x.to_string());
            let mut pp = Arc::clone(&mut p);

            let threadHandle = thread::Builder::new()
                .name(threadName.into())
                .spawn(move || {
                        println!("线程{}, hello",  thread::current().name().unwrap());

                        // while &pp.len > &0 {
                        //     let data = &pp.pop();

                            // let rs = &data.getX();
                            // println!("rs{}",  rs);
                        // }
                    let data = &pp.lock().unwrap().pop();
                    println!(" end");
                }).unwrap();
            threadHandle.join().unwrap();
            // thread_handles.push(threadHandle);
        }
        println!("22");

        // 返回的结果
        // let mut listVecNew = Vec::new();

        // // wait for threads
        // for threadHandle in thread_handles {
        //     //如果将 threadHandle.join() 设置在这里，则会先进行子线程，等待子线程结束后才会进入主线程执行任务
        //     let rs = threadHandle.join().unwrap();
        //     listVecNew.push(rs);
        // }
        println!("-----------------------------------------");
        println!("所有子线程全部执行完毕");
        println!("线程{} hello",  thread::current().name().unwrap());

        // // print result
        // println!("{}", &listVecNew.len());

        Ok(())
    }
}

